#include <platform.h>
#include <commapi.h>
#include "protocmd.h"
#include "crc16.h"
#include "p4p.h"

#define MAX_P4P_PACKET_SIZE 1400
#define MAX_BUFFER_LEN_P2P 64*1024+1024


#define CMD_P2P	0x93
#define P2P_HEAD_TAG	0x50
#pragma pack(push)
#pragma pack(1)
typedef struct _TCPHeader_{
	unsigned char HeaderTag[2];
	unsigned char Cmd;
	unsigned char CmdCheck;
	unsigned int Len;
}TTCPHeader, *PTCPHeader;
#pragma pack(pop)

typedef struct {
	TSessionHeader sessionH;
	UDK_S32 SendBufferLen;
	UDK_U8 *SendBuffer;
	UDK_S32 RecvBufferLen;
	UDK_U8 *RecvBuffer;
	int p4phandle;
	UDK_U16 quirks;
}ST_COMMOBJ_P4P,*PST_COMMOBJ_P4P;


const char* p4p_errorstring(int err){
	static const char* errstrs[]={		
		"ERROR_P4P_SUCCESS",
		"RRROR_P4P_INITIALIZED", 
		"ERROR_P4P_ALREADY_INITIALIZED",
		"ERROR_P4P_TIME_OUT",
		"ERROR_P4P_INVALID_ID",			
		"ERROR_P4P_INVALID_PARAMETER",	
		"ERROR_P4P_DEVICE_NOT_ONLINE",		
		"ERROR_P4P_FAIL_TO_RESOLVE_NAME",
		"ERROR_P4P_INVALID_PREFIX",	
		"ERROR_P4P_ID_OUT_OF_DATE",		
		"ERROR_P4P_NO_RELAY_SERVER_AVAILABLE",
		"ERROR_P4P_INVALID_SESSION_HANDLE",
		"ERROR_P4P_SESSION_CLOSED_REMOTE",
		"ERROR_P4P_SESSION_CLOSED_TIMEOUT",	
		"ERROR_P4P_SESSION_CLOSED_CALLED",
		"ERROR_P4P_REMOTE_SITE_BUFFER_FULL",	
		"ERROR_P4P_USER_LISTEN_BREAK",
		"ERROR_P4P_MAX_SESSION",	
		"ERROR_P4P_UDP_PORT_BIND_FAILED",
		"ERROR_P4P_USER_CONNECT_BREAK",	
		"ERROR_P4P_SESSION_CLOSED_INSUFFICIENT_MEMORY",
		"ERROR_P4P_INVALID_APILICENSE",
		"ERROR_P4P_FAIL_TO_CREATE_THREAD", 	
	};
	static const char* unkownerr="ERROR_P4P_GENERIC_ERR";
	if(err<0)err=-err;
	if(err>(-ERROR_P4P_FAIL_TO_CREATE_THREAD))
		return unkownerr;
	return errstrs[err];
}
#ifdef COMM_MODULE_AUTOINSTALL
UDK_HANDLE p4pcomm_INIT(PST_COMM_PARAMS param,const char *address, unsigned short PortNum, int speed)
#else
UDK_HANDLE EMBUDP_INIT(PST_COMM_PARAMS param,const char *address, unsigned short PortNum, int speed)
#endif
{
	UDK_S32 ret = 0;
	
	//connect to target now	
	ST_COMMOBJ_P4P *h=(PST_COMMOBJ_P4P)udk_malloc(sizeof(ST_COMMOBJ_P4P));
	if(h)
	{	
		int p4phandle;
		memset(h,0,sizeof(ST_COMMOBJ_P4P));
		ret =p4p_initialize();
		if(ERROR_P4P_SUCCESS!=ret){
			udk_free(h);
			return NULL;
		}
		p4phandle = p4p_connect(address);
		if(p4phandle<0) {
   		  UDK_LOGW("p4p connect %s\n",p4p_errorstring(h->p4phandle));
		  udk_free(h);
		  p4p_deinitialize();
		  return NULL;
		}
		h->p4phandle = p4phandle;		
		h->SendBufferLen = param->PackageSize;
		h->SendBuffer = (UDK_U8*)udk_malloc(h->SendBufferLen);
		h->RecvBufferLen = param->PackageSize;
		h->RecvBuffer = (UDK_U8*)udk_malloc(h->RecvBufferLen);
		h->quirks = param->Quirks;
	}
	return (UDK_HANDLE)h;
}

#ifdef COMM_MODULE_AUTOINSTALL
int p4pcomm_CLOSE(UDK_HANDLE handle)
#else
int EMBUDP_CLOSE(UDK_HANDLE handle)
#endif
{
	ST_COMMOBJ_P4P *h=(PST_COMMOBJ_P4P)(handle);
	p4p_close(h->p4phandle);
	p4p_deinitialize();
	if(h->SendBuffer){
		udk_free(h->SendBuffer);
	}
	
	if(h->RecvBuffer){
		udk_free(h->RecvBuffer);
	}
	
	if(h){
		udk_free(h);
	}
	return 1;
}
#ifdef COMM_MODULE_AUTOINSTALL
int p4pcomm_SEND(UDK_HANDLE handle,const char *Data, int Count)
#else
int EMBUDP_SEND(UDK_HANDLE handle,const char *Data, int Count)
#endif
{
    ST_COMMOBJ_P4P *h=(PST_COMMOBJ_P4P)(handle);
    int byteSent=0,sentLen=0,len =0;
    PTCPHeader ch=(PTCPHeader)(h->SendBuffer);
    int TimeOutMS=1000,ms=0;
    int lastTempErrorNo = 0;
    memset(h->SendBuffer, 0, sizeof(TTCPHeader));
    ch->HeaderTag[0]=P2P_HEAD_TAG; ch->HeaderTag[1]=P2P_HEAD_TAG;
    ch->Cmd=CMD_P2P;
    ch->CmdCheck=0xFF-CMD_P2P;
    ch->Len=Count;
    memcpy(h->SendBuffer+sizeof(TTCPHeader),Data,Count);	
    len = Count + sizeof(TTCPHeader);
    
    ms = udk_get_ticks();
    while(sentLen<len)
    {
      if((udk_get_ticks()-ms)>TimeOutMS){
	break;
      }
      byteSent = p4p_write(h->p4phandle , (char*)(h->SendBuffer+sentLen), len-sentLen);
      if(byteSent>0){
	if(byteSent>0&&(h->quirks&COMM_PARAM_QUIRK_DEBUG)){
		UDK_DUMP((const char*)("command packet ---->"),(const char*)(h->SendBuffer+sentLen),byteSent);
	}
	sentLen += byteSent;
      }
      else{
	UDK_LOGE("p4p write error %d\n",byteSent);
	  break;
	
      }
    }

    return sentLen;
}

#ifdef COMM_MODULE_AUTOINSTALL
int p4pcomm_READ(UDK_HANDLE handle,char *Data, int Count, int TimeOutMS)
#else
int EMBUDP_READ(UDK_HANDLE handle,char *Data, int Count, int TimeOutMS)
#endif
{
    ST_COMMOBJ_P4P *h=(PST_COMMOBJ_P4P)(handle);
	int len=0;
	UDK_U32 ms = 0;
	int nTotalLen = 0;
	PTCPHeader pTcpHeader = NULL;
	len = p4p_read(h->p4phandle,(char*)h->RecvBuffer,sizeof(TTCPHeader),TimeOutMS);
	if (len <= 0)
	{
		UDK_LOGW("read header timeout\n");
		return 0;
	}
	len = 0;
	pTcpHeader =(PTCPHeader)h->RecvBuffer;
	if(	pTcpHeader->HeaderTag[0]==P2P_HEAD_TAG && pTcpHeader->HeaderTag[1]==P2P_HEAD_TAG &&  
		pTcpHeader->Cmd == CMD_P2P && pTcpHeader->CmdCheck == (0xff-CMD_P2P))
	{
		int nRecvlen = 0;
		nTotalLen = pTcpHeader->Len;
		ms = udk_get_ticks();
		while(len < nTotalLen)
		{
			if((udk_get_ticks()-ms)>TimeOutMS)
			{
				UDK_LOGW("timeout\n");
				break;
			}
			nRecvlen = p4p_read(h->p4phandle,Data+len,((nTotalLen-len)>MAX_P4P_PACKET_SIZE)?MAX_P4P_PACKET_SIZE:nTotalLen-len,TimeOutMS);	
			if (nRecvlen < 0)
			{
				UDK_LOGW("p4p_read error =%d\n",nRecvlen);
				break;
			}
			len += nRecvlen;
		}
	}
	//dprintf("expect len=%d,but real len=%d, readlen =%d\r\n", Count, nTotalLen, len);
	return len;
}


#ifdef COMM_MODULE_AUTOINSTALL
int p4pcomm_SENDCMD(UDK_HANDLE handle,int Command, char *Data, int Count,PSessionHeader pshd)
#else
int EMBUDP_SENDCMD(UDK_HANDLE handle,int Command, char *Data, int Count,PSessionHeader pshd)
#endif
{
	ST_COMMOBJ_P4P *h=(PST_COMMOBJ_P4P)(handle);
	PProtoHeader pchd=(PProtoHeader)(h->SendBuffer);
	UDK_S32 sendLen = sizeof(TProtoHeader) + CRC_AND_ENDTAG_LEN + Count;
	UDK_S32 sentLen = 0;
	UDK_U8 *p;
		
	if(Count > h->SendBufferLen-PROTOCOL_AND_SESSION_LEN)	{
		return ERR_SEND_PACKAGE_BUF_INSUFFICIENT;
	}

	p = h->SendBuffer;
	//fill protocol header
	pchd->StartTag = PROTO_START_TAG;
	pchd->DestAddr = 0x1;
	pchd->Command = Command&0xff;
	pchd->DataSize = Count;
	//fill session header
	if(pshd){		
		memcpy(pchd+1,pshd,sizeof(TSessionHeader));		
		sendLen += sizeof(TSessionHeader);
		pchd->DataSize += sizeof(TSessionHeader);
		if(Count) memcpy((char *)(p+sizeof(TProtoHeader)+sizeof(TSessionHeader)), Data, Count);
	}else{
		if(Count) memcpy((char*)(p+sizeof(TProtoHeader)),Data,Count);
	}

	SETWORD(p+sizeof(TProtoHeader)+ pchd->DataSize,crc16(p+1, sendLen -sizeof(TSessionHeader)));
	*(p+sendLen-PROTOCOL_ENDTAG_LEN) = PROTO_END_TAG;
	sentLen = p4p_write(h->p4phandle,(char*)(h->SendBuffer), sendLen);
	if(sentLen>0&&(h->quirks&COMM_PARAM_QUIRK_DEBUG))
		UDK_DUMP((const char*)("command packet ---->"),(const char*)(h->SendBuffer),sentLen);
	return sentLen;
}

static int flush_io(int p4phandle)
{
	static char buf[1024];
	for(int i=0;i<10;i++)
		p4p_read(p4phandle,buf,1024,50);
	return 0;
}
#ifdef COMM_MODULE_AUTOINSTALL
int p4pcomm_READCMD(UDK_HANDLE Handle,int *Command,  char *Data, int *Count, int TimeOutMS)
#else
int EMBUDP_READCMD(UDK_HANDLE Handle,int *Command,  char *Data, int *Count, int TimeOutMS)
#endif
{
	PST_COMMOBJ_P4P h=(PST_COMMOBJ_P4P)Handle;
	PProtoHeader pchd = (PProtoHeader)h->RecvBuffer;
	int size=0;
	char *p=(char*)h->RecvBuffer;
	UDK_U32 msStartTime=0, msCurrentTime=0;
	int curLen=0,len=0;
	UDK_U32 hopeReplyID = h->sessionH.ReplyID;
	//start read the ptotocol header
	len = p4p_read(h->p4phandle,(char*)(h->RecvBuffer + curLen),sizeof(TProtoHeader),TimeOutMS);
	if(len > 0) {
		if(h->quirks&COMM_PARAM_QUIRK_DEBUG){
			UDK_DUMP((const char*)("response packet <----"),(const char*)(h->RecvBuffer + curLen),len);
		}
		if(pchd->StartTag == PROTO_START_TAG){
			curLen = sizeof(TProtoHeader);
			len = sizeof(TProtoHeader) + pchd->DataSize + CRC_AND_ENDTAG_LEN;
			if(len > h->RecvBufferLen){
				len = ERR_READ_PACKAGE_BUF_INSUFFICIENT;goto read_error;
			}
			msStartTime=udk_get_ticks();
			while(curLen<len){
				msCurrentTime = udk_get_ticks();
				if((msCurrentTime-msStartTime)>TimeOutMS){
					UDK_LOGE("Read data timeout %d. \n", TimeOutMS);
					break;
				}
				if(msCurrentTime <msStartTime){
					UDK_LOGE("GetTickCount error\n");
					break;
				}
				size = p4p_read(h->p4phandle,(char*)(h->RecvBuffer+curLen), len-curLen,1000);
				if(size>0){
					if(h->quirks&COMM_PARAM_QUIRK_DEBUG){	
						UDK_DUMP((const char*)("response packet <----"),(const char*)(h->RecvBuffer + curLen),size);
					}
					curLen += size;
				}

			}

			if(h->quirks&COMM_PARAM_QUIRK_DEBUG){
				UDK_LOGD("All data received. length %d. Total size is %d. \n", curLen,len);
			}
			if(curLen == len){
				//У���յ���ݵ�������
				UDK_U16 crc16r = GETWORD(p+len -CRC_AND_ENDTAG_LEN);
				UDK_U16 crc16l = crc16((UDK_U8*)(p+1),len -4);
				if(crc16r == crc16l){
					*Command = pchd->Command;
					*Count = pchd->DataSize - 4;
					memcpy(&(h->sessionH), p+sizeof(TProtoHeader), 4);
					memcpy(Data,p+sizeof(TProtoHeader) + 4, pchd->DataSize - 4);


				}else{
					UDK_LOGE( "crc error\n");
					flush_io(h->p4phandle);	//add by zuocs
					len = ERR_CMD_DATA_CRC_ERROR;
				}
			}else {
					//prot header data received and other data, but data length is incorrect
					UDK_LOGW("Remains received. Length %d incorrec,total %d\n", curLen,len);
					len = 0;
				}
			}else if(curLen == sizeof(TProtoHeader)){
				UDK_LOGW( "Sorry, only protocol data received\n");
				flush_io(h->p4phandle);		//add by zuocs
			}else{
				UDK_LOGW( "Sorry, none data received.\n");
				len = 0;
			}

	}
	
	if ((curLen > 0) && (len > 0) && (curLen >= 5) ){
		if (h->sessionH.ReplyID == hopeReplyID)	{
			UDK_LOGW( "Answer ID Correct.\n");
		} else {
			UDK_LOGW( "Answer ID wrong.\n");
		}
	}

	//UDK_LOGD( "The return value is %d. current length is %d\n", len, curLen);
	
read_error:
	return len;
}

#ifdef COMM_MODULE_AUTOINSTALL
int p4pcomm_PREREAD(UDK_HANDLE Handle)
#else
int EMBUDP_PREREAD(UDK_HANDLE Handle)
#endif
{
	return 0;
}

#ifdef COMM_MODULE_AUTOINSTALL
int p4pcomm_TERMINALREAD(UDK_HANDLE Handle)
#else
int EMBUDP_TERMINALREAD(UDK_HANDLE Handle)
#endif
{
	return 0;
}
#ifdef COMM_MODULE_AUTOINSTALL
const char * p4pcomm_NAME(UDK_HANDLE Handle)
#else
const char * EMBUDP_NAME(UDK_HANDLE Handle)
#endif
{
	return (const char*)"P4P";
}

#ifdef COMM_MODULE_AUTOINSTALL
int p4pcomm_ERROR(UDK_HANDLE Handle)
#else
int EMBUDP_ERROR(UDK_HANDLE Handle)
#endif
{
	return -1;
}

#ifdef COMM_MODULE_AUTOINSTALL
class CCommModuleP4P{
	public:

		CCommModuleP4P(){
			selfInstall();
		}
		virtual ~CCommModuleP4P(){
		  uninstall_comm_module((const char*)"P4P");
		}
		void selfInstall(void){			
			ST_COMM_MODULE my_comm={
					"P4P",//prot
					"p4pcomm",//libraryname
					p4pcomm_INIT,//init
					p4pcomm_CLOSE,//close
					p4pcomm_SEND,//send
					p4pcomm_READ,//read
					p4pcomm_TERMINALREAD,//terminalread
					p4pcomm_NAME,//name
					NULL,//flush
					NULL,//at->modem
					NULL,//at<-modem		
					p4pcomm_ERROR,//error
					p4pcomm_SENDCMD,//sendcmd
					p4pcomm_READCMD,//readcmd
			};
			install_comm_module(&my_comm);
		}

};
static CCommModuleP4P moduleInstance;
#endif
